﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using RAD;
using Microsoft.Xna.Framework;
using Red;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework.Graphics.PackedVector;

namespace TriExporterNET.Engine
{
 
	class EVEMesh
	{
		public List<EVEMeshPart> meshParts = new List<EVEMeshPart>();
		public Dictionary<string, Texture> textures = new Dictionary<string, Texture>();
		GraphicsDevice GraphicsDevice = null;


		public Vector3 modelCenter;
		public float modelRadius;
		public static TextureCube cube;
		static Random rnd = new Random((int)DateTime.Now.Ticks);

		BasicEffect effect;
        public EVEMesh(GraphicsDevice device, string redfile, List<Stuff.StuffFileEntry> List, bool black)
        {
            GraphicsDevice = device;
            effect = new BasicEffect(device);
            if (cube != null)
            {
                cube.Dispose();
            }
            var sfcl = List.Where(x => x.FullName.EndsWith("_cube.dds") && x.FullName.Contains("universe")).ToList();
            bool badcube = true;
            while (badcube)
            {
                var sfc = sfcl[rnd.Next(0, sfcl.Count - 1)];
                if (sfc != null)
                {
                    using (var stream = sfc.GetStream())
                    {
                        AlphaSubmarines.DDSLib.DDSFromStream(stream, device, 0, false, out  cube);
                        if (cube != null)
                            badcube = false;
                    }
                }
            }

            var sfred = List.FirstOrDefault(x => x.FullName == redfile);
            dynamic redobject;
            using (var stream = sfred.GetStream())
            {
                if (black)
                    redobject = new BlackObject(stream);
                else
                    redobject = new RedObject(stream);
            }
            dynamic mesh = null;
            try
            {
                mesh = redobject.highDetailMesh.object_;
            }
            catch { }
            if (mesh == null)
                try
                {
                    mesh = redobject.mesh;
                }
                catch { }
            string grfile = mesh.geometryResPath;
            grfile = grfile.Replace(":", "").Replace("/", "\\").Replace("\"", "").ToLower();
            var sfgr = List.FirstOrDefault(x => x.FullName == grfile);
            var bytes = sfgr.GetBytes();
            var gf = GrannyInterop.GrannyReadEntireFileFromMemory(bytes.Length, bytes);
            var fip = GrannyInterop.GrannyGetFileInfo(gf);
            var fi = fip.Convert<gFileInfo>();
            var mpp = fi.Meshes.Convert<IntPtr>();
            var mp = mpp.Convert<Mesh>();
            var vc = GrannyInterop.GrannyGetMeshVertexCount(mpp);
            var bpi = GrannyInterop.GrannyGetMeshBytesPerIndex(mpp);
            var ic = GrannyInterop.GrannyGetMeshIndexCount(mpp);
            var tgc = GrannyInterop.GrannyGetMeshTriangleGroupCount(mpp);
            var tgp = GrannyInterop.GrannyGetMeshTriangleGroups(mpp);

            IEnumerable<EVEMeshPart> parts = null;
            var inds = new int[ic];
            var handle = GCHandle.Alloc(inds, GCHandleType.Pinned);
            GrannyInterop.GrannyCopyMeshIndices(mpp, Marshal.SizeOf(inds[0]), handle.AddrOfPinnedObject());
            handle.Free();
            IndexBuffer = new IndexBuffer(device, inds[0].GetType(), ic, BufferUsage.WriteOnly);
            IndexBuffer.SetData(inds, 0, ic);
            parts = Enumerable.Range(0, tgc).Select(x => IntPtr.Add(tgp, Marshal.SizeOf(typeof(Groups)) * x).Convert<Groups>()).Select(tg =>
                {
                    return new EVEMeshPart() { TriangleCount = tg.TriCount, TriangleFirst = tg.TriFirst * 3 };
                }).ToArray();
            meshParts.AddRange(parts);

            VertexCount = vc;


            MVPTNTB[] vces = new MVPTNTB[vc];
            handle = GCHandle.Alloc(vces, GCHandleType.Pinned);
            GrannyInterop.GrannyCopyMeshVertices(mpp, GrannyInterop.GrannyPNGBT33332VertexType.Convert<IntPtr>(), handle.AddrOfPinnedObject());
            handle.Free();
            vertexBuffer = new VertexBuffer(device, typeof(MVPTNTB), vc, BufferUsage.WriteOnly);
            vertexBuffer.SetData<MVPTNTB>(vces);
            GrannyInterop.GrannyFreeFile(gf);
            IEnumerable<dynamic> areas = ((IEnumerable<dynamic>)mesh.opaqueAreas).Select(x => { x.isTransparent = false; return x; });
            try
            {
                areas = areas.Union(((List<dynamic>)mesh.transparentAreas).Select(x => { x.isTransparent = true; return x; }));
            }
            catch { }
            try
            {
                areas = areas.Union(((List<dynamic>)mesh.decalAreas).Select(x => { x.isTransparent = false; return x; }));
            }
            catch { }
            foreach (dynamic area in areas)
            {
                int i = 0;
                try
                {
                    i = int.Parse(area.index);
                }
                catch { }
                if (i >= meshParts.Count)
                    continue;
                var resources = ((List<dynamic>)area.effect.resources);
                var parameters = ((List<dynamic>)area.effect.parameters);
                meshParts[i].IsTransparent = (bool)area.isTransparent;
                dynamic dif = resources.FirstOrDefault(x => x.name.Replace("\"", "") == "DiffuseMap");
                var difs = dif.resourcePath.Replace(":", "").Replace("/", "\\").Replace("\"", "").ToLower();

                if (!textures.ContainsKey(difs))
                {
                    var sf = List.FirstOrDefault(x => x.FullName == difs);
                    if (sf != null)
                        using (var stream = sf.GetStream())
                        {
                            Texture2D tex = null;
                            AlphaSubmarines.DDSLib.DDSFromStream(stream, device, 0, false, out  tex);
                            textures[difs] = tex;
                            //textures[difs] = DDSSuport.Load(GraphicsDevice, stream);
                        }
                }
                if (textures.ContainsKey(difs))
                    meshParts[i].DiffuseMap = difs;
                dynamic glow = resources.FirstOrDefault(x => x.name.Replace("\"", "") == "GlowNormalSpecularMap");
                if (glow != null)
                {
                    var glows = glow.resourcePath.Replace(":", "").Replace("/", "\\").Replace("\"", "").ToLower();
                    if (!textures.ContainsKey(glows))
                    {
                        var sf = List.FirstOrDefault(x => x.FullName == glows);
                        if (sf != null)
                            using (var stream = sf.GetStream())
                            {
                                Texture2D tex = null;
                                AlphaSubmarines.DDSLib.DDSFromStream(stream, device, 0, false, out  tex);
                                textures[glows] = tex;
                                //textures[glows] = DDSSuport.Load(GraphicsDevice, stream);
                            }
                    }
                    if (textures.ContainsKey(glows))
                        meshParts[i].GlowNormalSpecularMap = glows;
                }

                dynamic pgs = resources.FirstOrDefault(x => x.name.Replace("\"", "") == "PgsMap");
                if (pgs != null)
                {
                    var pgss = pgs.resourcePath.Replace(":", "").Replace("/", "\\").Replace("\"", "").ToLower();
                    if (!textures.ContainsKey(pgss))
                    {
                        var sf = List.FirstOrDefault(x => x.FullName == pgss);
                        if (sf != null)
                            using (var stream = sf.GetStream())
                            {
                                Texture2D tex = null;
                                AlphaSubmarines.DDSLib.DDSFromStream(stream, device, 0, false, out  tex);
                                textures[pgss] = tex;
                                //textures[pgss] = DDSSuport.Load(GraphicsDevice, stream);
                            }
                    }
                    if (textures.ContainsKey(pgss))
                        meshParts[i].Pgs = pgss;
                }

                dynamic mask = resources.FirstOrDefault(x => x.name == "\"MaskMap\"");
                if (mask != null)
                {
                    var masks = mask.resourcePath.Replace(":", "").Replace("/", "\\").Replace("\"", "").ToLower();
                    if (!textures.ContainsKey(masks))
                    {
                        var sf = List.FirstOrDefault(x => x.FullName == masks);
                        if (sf != null)
                            using (var stream = sf.GetStream())
                            {
                                Texture2D tex = null;
                                AlphaSubmarines.DDSLib.DDSFromStream(stream, device, 0, false, out  tex);
                                textures[masks] = tex;
                                //textures[masks] = DDSSuport.Load(GraphicsDevice, stream);
                            }
                    }
                    if (textures.ContainsKey(masks))
                        meshParts[i].MaskMap = masks;
                }
                dynamic dc = parameters.FirstOrDefault(x => x.name.Replace("\"", "") == "MaterialDiffuseColor");
                if (dc != null)
                {
                    try
                    {
                        string dcs = dc.value.Replace("[", "").Replace("]", "");
                        meshParts[i].MaterialDiffuseColor = dcs.Split(',').Select(x => float.Parse(x, System.Globalization.CultureInfo.InvariantCulture)).ToArray();
                    }
                    catch
                    {
                        try
                        {
                            meshParts[i].MaterialDiffuseColor[0] = float.Parse(dc.v1, System.Globalization.CultureInfo.InvariantCulture);
                            meshParts[i].MaterialDiffuseColor[1] = float.Parse(dc.v2, System.Globalization.CultureInfo.InvariantCulture);
                            meshParts[i].MaterialDiffuseColor[2] = float.Parse(dc.v3, System.Globalization.CultureInfo.InvariantCulture);
                        }
                        catch { }
                    }
                }
                dynamic ms = parameters.FirstOrDefault(x => x.name.Replace("\"", "") == "MaskDiffuseColor");
                if (ms != null)
                {
                    try
                    {
                        string mss = ms.value.Replace("[", "").Replace("]", "");
                        meshParts[i].MaskDiffuseColor = mss.Split(',').Select(x => float.Parse(x, System.Globalization.CultureInfo.InvariantCulture)).ToArray();
                    }
                    catch
                    {
                        try
                        {
                            meshParts[i].MaskDiffuseColor[0] = float.Parse(ms.v1, System.Globalization.CultureInfo.InvariantCulture);
                            meshParts[i].MaskDiffuseColor[1] = float.Parse(ms.v2, System.Globalization.CultureInfo.InvariantCulture);
                            meshParts[i].MaskDiffuseColor[2] = float.Parse(ms.v3, System.Globalization.CultureInfo.InvariantCulture);
                        }
                        catch {
                            meshParts[i].MaskDiffuseColor = ms.value;
                        }
                    }
                }
                dynamic gc = parameters.FirstOrDefault(x => x.name == "\"GlowColor\"");
                if (gc != null)
                {
                    try
                    {
                        string gcs = gc.value.Replace("[", "").Replace("]", "");
                        meshParts[i].GlowColor = gcs.Split(',').Select(x => float.Parse(x, System.Globalization.CultureInfo.InvariantCulture)).ToArray();
                    }
                    catch
                    {
                        try
                        {
                            meshParts[i].GlowColor[0] = float.Parse(gc.v1, System.Globalization.CultureInfo.InvariantCulture);
                            meshParts[i].GlowColor[1] = float.Parse(gc.v2, System.Globalization.CultureInfo.InvariantCulture);
                            meshParts[i].GlowColor[2] = float.Parse(gc.v3, System.Globalization.CultureInfo.InvariantCulture);
                        }
                        catch { meshParts[i].GlowColor = gc.value; }
                    }
                }
            }
            try
            {
                string ffs = redobject.boundingSphereCenter.Replace("[", "").Replace("]", "");
                var ff = ffs.Split(',').Select(x => float.Parse(x, System.Globalization.CultureInfo.InvariantCulture)).ToArray();
                modelCenter = new Vector3(ff[0], ff[1], ff[2]);
            }
            catch {
                try { modelCenter = new Vector3(redobject.boundingSphereCenter[0], redobject.boundingSphereCenter[1], redobject.boundingSphereCenter[2]); }
                catch { }
                modelCenter = new Vector3(0, 0, 0); }
            try
            {
                modelRadius = redobject.boundingSphereRadius;
            }
            catch
            {
                try
                {
                    modelRadius = float.Parse(redobject.boundingSphereRadius, System.Globalization.CultureInfo.InvariantCulture);
                }
                catch { }
            }
        }


		[StructLayout(LayoutKind.Sequential)]
		public struct MVPTNTB : IVertexType
		{
			public MVPTNTB(Vector3 p, Vector3 n, Vector3 tg, Vector3 bn, Vector2 t)
				: this()
			{
				Position = p;
				TexCoord = t;
				Normal = n;
				Tangent = tg;
				Binormal = bn;
			}
			public Vector3 Position;
			public Vector3 Normal;
			public Vector3 Tangent;
			public Vector3 Binormal;
			public Vector2 TexCoord;

			public VertexDeclaration VertexDeclaration
			{
				get
				{
					return new VertexDeclaration(new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
						new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
						new VertexElement(24, VertexElementFormat.Vector3, VertexElementUsage.Tangent, 0),
						new VertexElement(36, VertexElementFormat.Vector3, VertexElementUsage.Binormal, 0),
						new VertexElement(48, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
						);
				}
			}
		}

		public VertexBuffer vertexBuffer = null;
		public int VertexCount = 0;
		public IndexBuffer IndexBuffer = null;

		public class EVEMeshPart
		{
			//public IndexBuffer IndexBuffer;
			public int TriangleCount;
			public int TriangleFirst;
			public string DiffuseMap;
			public string GlowNormalSpecularMap;
			public string Pgs;
			public string MaskMap;
			public float[] MaskDiffuseColor = new float[4] { 0.5f, 0.5f, 0.5f, 1f };
			public float[] MaterialDiffuseColor = new float[4] { 0.5f, 0.5f, 0.5f, 1f };
			public float[] GlowColor = new float[4] { 0f, 0f, 0f, 1f };
			public bool IsTransparent;

		}
	}
}
